package com.lagou.seckill.common.utils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.lagou.seckill.common.enums.ErrorCodeEnum;
import com.lagou.seckill.common.exceptions.CustomException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Slf4j
public class OkHttpUtils {

	public static final String APPLICATION_JSON_CHARSET_UTF_8 = "application/json; charset=utf-8";

	private OkHttpUtils() {
		throw new CustomException.ServiceException(ErrorCodeEnum.SYSTEM_ERROR_B0801);
	}

	public static String get(Integer readTimeout, Map<String, String> map, String url, int retryTime) {
		return doGet(readTimeout, map, url, retryTime);
	}

	/**
	 * get请求
	 *
	 * @param url
	 * @return
	 */
	public static String get(Integer readTimeout, Map<String, String> map, String url) {
		return doGet(readTimeout, map, url, 0);
	}

	/**
	 * get请求
	 *
	 * @param url
	 * @return
	 */
	public static <R> String get(Integer readTimeout, R request, String url) {
		Map<String, String> map;
		if (null != request) {
			map = JSONUtils.read(JSONUtils.write(request), new TypeReference<Map<String, String>>() {
			});
		} else {
			map = null;
		}

		return doGet(readTimeout, map, url, 0);
	}

	@Nullable
	private static String doGet(Integer readTimeout, Map<String, String> map, String url, int retryTime) {
		log.info("ohttp请求url:{},参数：{}", url, JSONUtils.write(map));

		OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS);
		if (retryTime > 0) {
			okHttpClientBuilder.addInterceptor(new RetryIntercepter(retryTime));
		}
		OkHttpClient client = okHttpClientBuilder.build();
		HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
		if (map != null) {
			for (Map.Entry<String, String> entry : map.entrySet()) {
				builder.addQueryParameter(entry.getKey(), entry.getValue());
			}
		}

		Request.Builder reqBuilder = new Request.Builder()
				.url(builder.build().toString());
		Request req = reqBuilder.build();

		try {
			Response response = client.newCall(req).execute();
			ResponseBody responseBody = response.body();
			String responseStr;
			if (responseBody != null) {
				responseStr = responseBody.string();
			} else {
				responseStr = null;
			}
			log.info("okhttp请求返回参数:{} ", responseStr);

			return responseStr;
		} catch (Exception e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}
	}

	/**
	 * 参数以对象的形式
	 *
	 * @param readTimeout
	 * @param request
	 * @param url
	 * @param retryTime
	 * @return
	 */
	public static byte[] doGetReturnByteArray(Integer readTimeout, Object request, String url, int retryTime, boolean removeNullValue) {
		Map<String, String> map;
		if (null != request) {
			map = JSONUtils.read(JSONUtils.write(request), new TypeReference<Map<String, String>>() {
			});
		} else {
			map = null;
		}
		// 移除值为空的map
		if (map != null && removeNullValue) {
			Set set = map.keySet();
			for (Iterator iterator = set.iterator(); iterator.hasNext();) {
				Object obj = iterator.next();
				String value = map.get(obj);
				if (StringUtils.isBlank(value)) {
					// 过滤掉为null和""的值
					iterator.remove();
				}
			}
		}
		return doGetReturnByteArray(readTimeout, map, url, retryTime);
	}

	/**
	 * 返回字节数组
	 *
	 * @param readTimeout
	 * @param map
	 * @param url
	 * @param retryTime
	 * @return
	 */
	private static byte[] doGetReturnByteArray(Integer readTimeout, Map<String, String> map, String url, int retryTime) {
		log.info("ohttp请求url:{},参数：{}", url, JSONUtils.write(map));

		OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS);
		if (retryTime > 0) {
			okHttpClientBuilder.addInterceptor(new RetryIntercepter(retryTime));
		}
		OkHttpClient client = okHttpClientBuilder.build();
		HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
		if (map != null) {
			for (Map.Entry<String, String> entry : map.entrySet()) {
				builder.addQueryParameter(entry.getKey(), entry.getValue());
			}
		}

		Request.Builder reqBuilder = new Request.Builder()
				.url(builder.build().toString());
		Request req = reqBuilder.build();

		try {
			Response response = client.newCall(req).execute();
			ResponseBody responseBody = response.body();
			byte[] bytes = new byte[0];
			if (responseBody != null) {
				bytes = responseBody.bytes();
			}
			return bytes;
		} catch (Exception e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}
	}

	public static String postJsonStr(Integer readTimeout, String jsonStr, String url) {
		log.info("ohttp请求url:{},参数：{}", url, jsonStr);

		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS)
				.build();
		MediaType mediaType = MediaType.parse(APPLICATION_JSON_CHARSET_UTF_8);
		RequestBody requestBody = RequestBody.create(mediaType, jsonStr);
		Request req = new Request.Builder().url(url).post(requestBody).build();
		Call call = client.newCall(req);
		try {
			Response response = call.execute();
			ResponseBody responseBody = response.body();
			String responseStr;
			if (responseBody != null) {
				responseStr = responseBody.string();
			} else {
				responseStr = null;
			}
			log.info("okhttp请求返回参数:{} ", responseStr);
			return responseStr;
		} catch (IOException e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}
	}

	public static <R> String postJson(Integer readTimeout, R request, String url) {
		log.info("okhttp请求url:{},参数：{}", url, JSONUtils.write(request));

		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS)
				.build();
		MediaType mediaType = MediaType.parse(APPLICATION_JSON_CHARSET_UTF_8);

		RequestBody requestBody = RequestBody.create(mediaType, JSONUtils.write(request));
		Request req = new Request.Builder().url(url).post(requestBody).build();
		Call call = client.newCall(req);
		try {
			Response response = call.execute();
			ResponseBody responseBody = response.body();
			String responseStr;
			if (responseBody != null) {
				responseStr = responseBody.string();
			} else {
				responseStr = null;
			}
			log.info("okhttp请求返回参数:{} ", responseStr);
			return responseStr;
		} catch (IOException e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}

	}

	public static <R> Response postForResponse(Integer timeout, R request, String url) throws IOException {
		return doPost(timeout,request,url);
	}

	public static <R> String post(Integer timeout, R request, String url) throws IOException {
		Response response = doPost(timeout,request,url);
		String responseStr = response.body().string();
		log.info("请求返回参数:{} ", responseStr);
		return responseStr;
	}

	private static <R> Response doPost(Integer timeout, R request, String url) throws IOException{
		log.info("okhttp请求url:{},参数：{}", url, JSONUtils.write(request));
		OkHttpClient client;
		if (timeout != null && timeout > 0) {
			client = new OkHttpClient().newBuilder()
					.readTimeout(timeout, TimeUnit.SECONDS)
					.connectTimeout(timeout, TimeUnit.SECONDS)
					.build();
		} else {
			client = new OkHttpClient();
		}
		MediaType mediaType = MediaType.parse(APPLICATION_JSON_CHARSET_UTF_8);
		RequestBody requestBody = RequestBody.create(mediaType, JSONUtils.write(request));
		Request req = new Request.Builder().url(url).post(requestBody).build();
		Call call = client.newCall(req);
		return call.execute();
	}

	public static <R> Response getForResponse(Integer readTimeout, R request, String url) {
		log.info("okhttp请求url:{},参数：{}", url, JSONUtils.write(request));

		Map<String, String> map = JSONUtils.read(JSONUtils.write(request), new TypeReference<Map<String, String>>() {
		});
		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS)
				.build();
		HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
		for (Map.Entry<String, String> entry : map.entrySet()) {
			builder.addQueryParameter(entry.getKey(), entry.getValue());
		}

		Request req = new Request.Builder().url(builder.build().toString()).build();
		try {
			return client.newCall(req).execute();
		} catch (Exception e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}
	}

	public static String uploadFilePost(String url, File temporaryFile) {
		log.info("okhttp的uploadFilePost请求url:{}", url);

		if (!temporaryFile.exists()) {
			return "";
		}
		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(180, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS)
				.build();
		RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
				.addFormDataPart("file", temporaryFile.getName(),
						RequestBody.create(MediaType.parse("application/octet-stream"),
								temporaryFile))
				.build();
		Request request = new Request.Builder()
				.url(url)
				.post(body)
				.build();

		Response response;
		try {
			response = client.newCall(request).execute();
			String jsonString = response.body().string();
			if (response.isSuccessful()) {
				return jsonString;
			}
		} catch (Exception e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0001, e);
		}
		return "";
	}


	/**
	 * get请求
	 *
	 * @param url
	 * @return
	 */
	public static InputStream download(Integer readTimeout, String url) {

		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(readTimeout == null ? 180 : readTimeout, TimeUnit.SECONDS)
				.connectTimeout(5, TimeUnit.SECONDS)
				.build();

		Request req = new Request.Builder().url(url).build();
		try {
			Response response = client.newCall(req).execute();
			if (response.body() != null) {
				return response.body().byteStream();
			} else {
				throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0700.getCode(), "返回为空");
			}
		} catch (Exception e) {
			throw new CustomException.ServiceException(ErrorCodeEnum.SERVICE_ERROR_C0700, e);
		}
	}

	public static <R> String post(Integer timeout, R request, String url, Integer retryTime,Long retryInterval) throws IOException {
		return doPost(timeout, request, url, retryTime,retryInterval);
	}

	public static <R> String post(Integer timeout, R request, String url, Integer retryTime) throws IOException {
		// log.info("okhttp请求url:{},参数：{}",url, JSON.toJSONString(request));
		return doPost(timeout, request, url, retryTime,null);
	}

	private static <R> String doPost(Integer timeout, R request, String url, Integer retryTime,Long retryInterval) throws IOException {
		OkHttpClient client;
		if (timeout != null && timeout > 0) {
			client = new OkHttpClient().newBuilder()
					.addInterceptor(new RetryIntercepter(retryTime,retryInterval))
					.readTimeout(timeout, TimeUnit.SECONDS)
					.connectTimeout(timeout, TimeUnit.SECONDS)
					.build();
		} else {
			client = new OkHttpClient();
		}
		MediaType mediaType = MediaType.parse(APPLICATION_JSON_CHARSET_UTF_8);
		RequestBody requestBody = RequestBody.create(mediaType, JSONUtils.write(request));
		Request req = new Request.Builder().url(url).post(requestBody).build();
		Call call = client.newCall(req);
		Response response = null;
		String responseStr = null;
		try {
			response = call.execute();
			responseStr = response.body().string();
		} catch (IOException e) {
			log.error("请求返回参数:{} ", responseStr, e);
			throw e;
		}
		return responseStr;
	}

	public static <R> String postByFormData(Integer timeout, String url, Map<String, Object> params) throws IOException {

		String resp = "";
		log.error("请求参数:{} ", params);
		if (params == null) {
			params = new HashMap<>();
		}

		OkHttpClient client = new OkHttpClient().newBuilder()
				.readTimeout(timeout, TimeUnit.SECONDS)
				.connectTimeout(timeout, TimeUnit.SECONDS)
				.build();

		MultipartBody.Builder builder = new MultipartBody.Builder()
				.setType(MultipartBody.FORM);

		params.entrySet().stream().forEach(entry -> {
			builder.addFormDataPart(entry.getKey(), String.valueOf(entry.getValue()));
		});

		RequestBody body = builder.build();

		Request request = new Request.Builder()
				.url(url)
				.method("POST", body)
				.build();
		try {
			Response response = client.newCall(request).execute();
			resp = response.body().string();
		} catch (IOException e) {
			log.error("请求返回参数:{} ", resp, e);
			throw e;
		}

		return resp;

	}

}
